package service

import (
	"fmt"
	"github.com/savsgio/atreugo/v11"
	"hotter/dynamicController"
	"hotter/model"
	"hotter/variable"
	"io/ioutil"
	"log"
	"os"
	"runtime/debug"
	"strconv"
	"strings"
	"sync"
)

type HotterService struct {
	DynamicController *dynamicController.DynamicController
}

func NewHotterService() *HotterService {
	return &HotterService{
		DynamicController: dynamicController.NewDynamicController(),
	}
}

func (hs *HotterService) HotRenewal(versionNumber string, platform string) map[string]interface{} {

	var hcis []*model.HeatChangeInformation
	variable.Db.Where("platform = ?", platform).Order("created_at desc").Find(&hcis)

	paths := strings.Split(hcis[0].FileName, "\\")
	newest_numbers := strings.Split(strings.Split(paths[len(paths)-1], "_")[1], ".")
	numbers := strings.Split(versionNumber, ".")

	for i := 0; i < len(numbers); i++ {
		number_int, _ := strconv.Atoi(numbers[i])
		newest_number_int, _ := strconv.Atoi(newest_numbers[i])
		if number_int < newest_number_int {
			return map[string]interface{}{
				"code":    200,
				"message": "成功",
				"data": map[string]string{
					"fileName": hcis[0].FileName,
					"paths":    paths[len(paths)-1],
				},
			}
		}
	}

	return map[string]interface{}{"code": 200, "message": "已是最新版本", "data": nil}
}

func (hs *HotterService) GetFileInfo(folder string) *model.FileInfo {
	fileInfo, err := os.Stat(folder)
	if os.IsNotExist(err) {
		fmt.Println("file not exist")
	}

	return &model.FileInfo{
		Name:        fileInfo.Name(),
		IsDir:       fileInfo.IsDir(),
		ModTime:     fileInfo.ModTime(),
		Size:        fileInfo.Size(),
		SubFileInfo: []*model.FileInfo{},
	}
}

func (hs *HotterService) GetSubFilesAsync(folder string, fi *model.FileInfo, wg *sync.WaitGroup, lock *sync.Mutex) {
	files, _ := ioutil.ReadDir(folder)
	//var wg sync.WaitGroup
	for _, file := range files {
		file := file
		wg.Add(1)
		go func(wg *sync.WaitGroup) {
			defer func() {
				err := recover()
				if err != nil {
					log.Println("range sub files error：", err)
					return
				}
			}()

			sfr := "/fileAsync" + strings.Split(folder, ":")[1] + "/" + file.Name()
			lock.Lock()
			if _, ok := FILE_SRV_ROUTER_MAP[sfr]; !ok {
				variable.Server.GET(sfr, hs.DynamicController.GetCatalogueInfoByLevelAsync)
				FILE_SRV_ROUTER_MAP[sfr] = struct{}{}
			}
			lock.Unlock()

			sfi := hs.GetFileInfo(folder + "/" + file.Name())
			fi.SubFileInfo = append(fi.SubFileInfo, sfi)

			if file.IsDir() {
				hs.GetSubFilesAsync(folder+"/"+file.Name(), sfi, wg, lock)
			} else {
				sfi.Details = hs.ReadFileDetail(folder + "/" + file.Name())
			}

			wg.Done()
		}(wg)
	}
	//wg.Wait()
}

func printStackTrace() {
	stack := string(debug.Stack())
	fmt.Println(stack)
}

func (hs *HotterService) GetSubFilesAsyncGroup(folder string, fi *model.FileInfo, wg *sync.WaitGroup, lock *sync.Mutex, gop *atreugo.Router) {
	files, _ := ioutil.ReadDir(folder)

	fr := strings.Split(folder, ":")[1]
	last := strings.Split(fr, "/")
	ngp := last[len(last)-1]

	var group *atreugo.Router
	//var wg sync.WaitGroup

	if len(files) > 0 && gop != nil {
		group = gop.NewGroupPath("/" + ngp)
	}

	for _, file := range files {
		file := file

		wg.Add(1)
		go func(wg *sync.WaitGroup) {
			defer func() {
				err := recover()
				if err != nil {
					log.Println("range sub files error：", err)
					printStackTrace()
					return
				}
			}()

			sfr := "/fileAsync" + fr + "/" + file.Name()
			lock.Lock()
			if _, ok := FILE_SRV_ROUTER_MAP[sfr]; !ok && group != nil {
				group.GET("/"+file.Name(), hs.DynamicController.GetCatalogueInfoByLevelAsync)
				FILE_SRV_ROUTER_MAP[sfr] = struct{}{}
			}
			lock.Unlock()

			sfi := hs.GetFileInfo(folder + "/" + file.Name())
			fi.SubFileInfo = append(fi.SubFileInfo, sfi)

			if file.IsDir() {
				hs.GetSubFilesAsyncGroup(folder+"/"+file.Name(), sfi, wg, lock, group)
			} else {
				sfi.Details = hs.ReadFileDetail(folder + "/" + file.Name())
			}

			wg.Done()
		}(wg)
	}
	//wg.Wait()
}

func (hs *HotterService) GetSubFiles(folder string, fi *model.FileInfo) {
	files, _ := ioutil.ReadDir(folder)
	for _, file := range files {
		variable.Server.GET("/file"+strings.Split(folder, ":")[1]+"/"+file.Name(), hs.DynamicController.GetCatalogueInfoByLevel)

		sfi := hs.GetFileInfo(folder + "/" + file.Name())
		fi.SubFileInfo = append(fi.SubFileInfo, sfi)

		if file.IsDir() {
			hs.GetSubFiles(folder+"/"+file.Name(), sfi)
		} else {
			sfi.Details = hs.ReadFileDetail(folder + "/" + file.Name())
		}
	}
}

func (hs *HotterService) ReadFileDetail(folder string) string {
	f, err := os.Open(folder)
	if err != nil {
		fmt.Println("read file fail", err)
		return "读取失败"
	}
	defer f.Close()

	fd, err := ioutil.ReadAll(f)
	if err != nil {
		fmt.Println("read to fd fail", err)
		return "读取字节失败"
	}

	return string(fd)
}

var FILE_SRV_ROUTER_MAP = map[string]struct{}{}

func (hs *HotterService) GetFilesAsync(folder string) []*model.FileInfo {
	defer func() {
		err := recover()
		if err != nil {
			log.Println("GetFiles error：", err)
			return
		}
	}()

	var (
		wg   sync.WaitGroup
		lock sync.Mutex
	)

	br := strings.Split(folder, ":")[1]
	fr := "/fileAsync" + br
	lock.Lock()
	if _, ok := FILE_SRV_ROUTER_MAP[fr]; !ok {
		variable.Server.GET(fr, hs.DynamicController.GetCatalogueInfoByLevelAsync)
		FILE_SRV_ROUTER_MAP[fr] = struct{}{}
	}
	lock.Unlock()

	var file_list []*model.FileInfo
	files, _ := ioutil.ReadDir(folder)
	for _, file := range files {
		file := file
		wg.Add(1)
		go func(wg *sync.WaitGroup) {
			defer func() {
				err := recover()
				if err != nil {
					log.Println("range files error：", err)
					return
				}
			}()

			sfr := fr + "/" + file.Name()
			lock.Lock()
			if _, ok := FILE_SRV_ROUTER_MAP[sfr]; !ok {
				variable.Server.GET(sfr, hs.DynamicController.GetCatalogueInfoByLevelAsync)
				FILE_SRV_ROUTER_MAP[sfr] = struct{}{}
			}
			lock.Unlock()

			fi := hs.GetFileInfo(folder + "/" + file.Name())
			file_list = append(file_list, fi)

			if file.IsDir() {
				hs.GetSubFilesAsync(folder+"/"+file.Name(), fi, wg, &lock)
			} else {
				fi.Details = hs.ReadFileDetail(folder + "/" + file.Name())
			}

			wg.Done()
		}(&wg)
	}
	wg.Wait()

	return file_list
}

func (hs *HotterService) GetFilesAsyncGroup(folder string) []*model.FileInfo {
	defer func() {
		err := recover()
		if err != nil {
			log.Println("GetFiles error：", err)
			printStackTrace()
			return
		}
	}()

	var (
		wg        sync.WaitGroup
		lock      sync.Mutex
		baseGroup *atreugo.Router
		group     *atreugo.Router
	)

	files, _ := ioutil.ReadDir(folder)

	lock.Lock()
	if _, ok := FILE_SRV_ROUTER_MAP["/fileAsync"]; !ok {
		baseGroup = variable.Server.NewGroupPath("/fileAsync")
		FILE_SRV_ROUTER_MAP["/fileAsync"] = struct{}{}
	}
	lock.Unlock()

	br := strings.Split(folder, ":")[1]
	fr := "/fileAsync" + br

	lock.Lock()
	if _, ok := FILE_SRV_ROUTER_MAP[fr]; !ok && baseGroup != nil {
		baseGroup.GET(br, hs.DynamicController.GetCatalogueInfoByLevelAsync)
		if len(files) > 0 {
			group = baseGroup.NewGroupPath(br)
		}
		FILE_SRV_ROUTER_MAP[fr] = struct{}{}
	}
	lock.Unlock()

	var file_list []*model.FileInfo
	for _, file := range files {
		file := file

		wg.Add(1)
		go func(wg *sync.WaitGroup) {
			defer func() {
				err := recover()
				if err != nil {
					log.Println("range files error：", err)
					printStackTrace()
					return
				}
			}()

			sfr := fr + "/" + file.Name()
			lock.Lock()
			if _, ok := FILE_SRV_ROUTER_MAP[sfr]; !ok && group != nil {
				group.GET("/"+file.Name(), hs.DynamicController.GetCatalogueInfoByLevelAsync)
				FILE_SRV_ROUTER_MAP[sfr] = struct{}{}
			}
			lock.Unlock()

			fi := hs.GetFileInfo(folder + "/" + file.Name())
			file_list = append(file_list, fi)

			if file.IsDir() {
				hs.GetSubFilesAsyncGroup(folder+"/"+file.Name(), fi, wg, &lock, group)
			} else {
				fi.Details = hs.ReadFileDetail(folder + "/" + file.Name())
			}

			wg.Done()
		}(&wg)
	}
	wg.Wait()

	return file_list
}

func (hs *HotterService) GetFiles(folder string) []*model.FileInfo {
	defer func() {
		err := recover()
		if err != nil {
			log.Println("GetFiles error：", err)
			return
		}
	}()

	variable.Server.GET("/file"+strings.Split(folder, ":")[1], hs.DynamicController.GetCatalogueInfoByLevel)

	var file_list []*model.FileInfo
	files, _ := ioutil.ReadDir(folder)
	for _, file := range files {
		variable.Server.GET("/file"+strings.Split(folder, ":")[1]+"/"+file.Name(), hs.DynamicController.GetCatalogueInfoByLevel)

		fi := hs.GetFileInfo(folder + "/" + file.Name())
		file_list = append(file_list, fi)

		if file.IsDir() {
			hs.GetSubFiles(folder+"/"+file.Name(), fi)
		} else {
			fi.Details = hs.ReadFileDetail(folder + "/" + file.Name())
		}
	}

	return file_list
}
